#include <memory>

#include <wx/sstream.h>

#include "md5.h"

#include "MsnSession.h"
#include "MsnConnection.h"
#include "MsnCommand.h"
#include "MsnMessenger.h"
#include "MsnContact.h"

#include "TCPTransport.h"

#include "ContactManager.h"
#include "SessionManager.h"

#include "Util.h"
#include "UserConfig.h"

#include "ServerConnection.h"
#include "SessionConnection.h"


ServerConnection::ProcessorMap ServerConnection::processorMap;

ServerConnection::ServerConnection(MsnMessenger* mm) : messenger(mm)
{
	serverType = _T("NS");
}

ServerConnection::~ServerConnection()
{
	DEBUG(_T("~ServerConnection"));

}

bool ServerConnection::InitProcessorMap()
{
	DEBUG(_T("init processor map..."));

	processorMap[_T("VER")] = &ServerConnection::ProcessVER;
	processorMap[_T("CVR")] = &ServerConnection::ProcessCVR;
	processorMap[_T("USR")] = &ServerConnection::ProcessUSR;
	processorMap[_T("MSG")] = &ServerConnection::ProcessMSG;
	processorMap[_T("PRP")] = &ServerConnection::ProcessPRP;
	

	processorMap[_T("CHL")] = &ServerConnection::ProcessCHL;

	processorMap[_T("SYN")] = &ServerConnection::ProcessSYN;
	processorMap[_T("PRP")] = &ServerConnection::ProcessPRP;
	processorMap[_T("LSG")] = &ServerConnection::ProcessLSG;
	processorMap[_T("LST")] = &ServerConnection::ProcessLST;

	processorMap[_T("ILN")] = &ServerConnection::ProcessILN;
	processorMap[_T("NLN")] = &ServerConnection::ProcessNLN;
	processorMap[_T("FLN")] = &ServerConnection::ProcessFLN;

	processorMap[_T("CHG")] = &ServerConnection::ProcessCHG;
	processorMap[_T("ADC")] = &ServerConnection::ProcessADC;
	processorMap[_T("ADG")] = &ServerConnection::ProcessADG;
	processorMap[_T("REM")] = &ServerConnection::ProcessREM;
	processorMap[_T("RMG")] = &ServerConnection::ProcessRMG;
	processorMap[_T("REG")] = &ServerConnection::ProcessREG;

	processorMap[_T("XFR")] = &ServerConnection::ProcessXFR;
	processorMap[_T("RNG")] = &ServerConnection::ProcessRNG;

	return true;
}

ServerConnection::Processor ServerConnection::FindProcessor(const wxString& cmdType)
{
	static bool done = InitProcessorMap();
	
	ProcessorMap::iterator it = processorMap.find(cmdType);

	if(it != processorMap.end()){
		return (*it).second;
	}else{
		return NULL;
	}
}


void ServerConnection::ConnectionOpened()
{
    OutCommandVER ver;
    ver.SetVersion(_T("MSNP10"));
    ver.SetVersion(_T("CVR0"));
    SendCommand(ver);
}

void ServerConnection::ConnectionFailed()
{
    messenger->OnConnectionFailed();
}

void ServerConnection::ConnectionClosed()
{
    //messenger->OnSignedOut();
}

void ServerConnection::ConnectionBroken()
{
    messenger->OnConnectionBroken();
}

/*
void ServerConnection::ProcessCommand(const wxString& line)
{
	//WARN(line);

	if(line.StartsWith(_T("USR"))){
//		WARN(line);
	}

	//can't use auto_ptr. why???
	MsnCommand* cmd = MsnCommandFactory::Get().CreateCommand(line);

	if(!cmd){
		DEBUG(_T("undefined command: ")+line);
		return;
	}

	Processor p = FindProcessor(cmd->GetCommand());

	if(p){
//		(this->*p)(cmd);
	}else{
		DEBUG(_T("unprocessed command: ")+line);
	}

	delete cmd;
}
*/

void ServerConnection::ProcessCommand(MsnCommandWrapper* cmdWrapper)
{
	wxString type = cmdWrapper->GetCommand()->GetType();

	Processor p = FindProcessor(type);

	if(p){
		(this->*p)(cmdWrapper);
	}else{
		DEBUG(_T("unprocessed command: ") + type);
	}
}

void ServerConnection::ProcessVER(MsnCommandWrapper* wrapper)
{
	wxString client = _T("0x0412 ");
	client<<SystemUtil::GetOSName().BeforeFirst(' ')<<_T(" ");
	client<<SystemUtil::GetOSVersion()<<_T(" ");
	client<<_T("x86 Tuhitty 0.1 MSMSGS");

    OutCommandCVR cvr;
    cvr.SetClientInfo(client);
    cvr.SetPassport(userPassport);
    SendCommand(cvr);
}

void ServerConnection::ProcessCVR(MsnCommandWrapper* wrapper)
{
    OutCommandNotifyUSR usr;
    usr.SetPassport(userPassport);
    SendCommand(usr);
}

void ServerConnection::ProcessUSR(MsnCommandWrapper* wrapper)
{
	InCommandUSR* cmd = dynamic_cast<InCommandUSR*>(wrapper->GetCommand());

	//!!!!!!!!!!!!!! output something or seems the socket will be blocked.
	//WARN(_T(""));
	//!!!!!!!!!!!!!!!!!

    if(cmd->IsOK()){
		messenger->OnSignedIn(cmd->GetPassport());
    }else{
        wxString auth = cmd->GetAuthString();
        wxString ticket = HttpUtil::GetTicket(auth, userPassport, userPassword);

        DEBUG(_T("ticket == ")+ticket);

        if(!ticket.IsEmpty()){
            OutCommandAuthUSR usr;
			usr.SetTicket(ticket);
			SendCommand(usr);
		}else{
            this->Close();
			
			//wxCommandEvent event(EVT_MSN_SERVER_AUTHETICATION_FAILED, -1);
			messenger->OnAuthenticationFailed();
		}
    }
}

void ServerConnection::ProcessSYN(MsnCommandWrapper* wrapper)
{
	//WARN(_T("SYN"));

	InCommandSYN* cmd = dynamic_cast<InCommandSYN*>(wrapper->GetCommand());

    //messenger->GetContactManager()->SyncStart(cmd->GetVersion(), cmd->GetGroupCount(), cmd->GetContactCount());
	messenger->OnSynchronizationReceived(cmd->GetVersion(), cmd->GetGroupCount(), cmd->GetContactCount());
}

void ServerConnection::ProcessLSG(MsnCommandWrapper* wrapper)
{
	InCommandLSG* cmd = dynamic_cast<InCommandLSG*>(wrapper->GetCommand());

    MsnGroup* group = new MsnGroup(cmd->GetGroupID(), cmd->GetGroupName());

    //messenger->GetContactManager()->ListGroup(group);
	messenger->OnGroupReceived(group);
}

void ServerConnection::ProcessLST(MsnCommandWrapper* wrapper)
{
	InCommandLST* cmd = dynamic_cast<InCommandLST*>(wrapper->GetCommand());

    MsnContact* contact = new MsnContact();
    contact->SetID(cmd->GetContactID());
    contact->SetPassport(cmd->GetPassport());
    contact->SetName(cmd->GetName());
    contact->SetGroupList(cmd->GetGroupID());
    contact->SetListNumber(cmd->GetListNumber());
    contact->SetStatus(wxEmptyString);

    //messenger->GetContactManager()->ListContact(contact);
	messenger->OnContactReceived(contact);
}

void ServerConnection::ProcessILN(MsnCommandWrapper* wrapper)
{
	InCommandILN* cmd = (InCommandILN*)wrapper->GetCommand();

	messenger->OnContactNameChanged(cmd->GetPassport(), cmd->GetName());
	messenger->OnContactStatusChanged(cmd->GetPassport(), cmd->GetStatus());

	messenger->OnContactObjectChanged(cmd->GetPassport(), cmd->GetMsnObject());
}

void ServerConnection::ProcessNLN(MsnCommandWrapper* wrapper)
{
	InCommandNLN* cmd = (InCommandNLN*)wrapper->GetCommand();

	messenger->OnContactNameChanged(cmd->GetPassport(), cmd->GetName());
	messenger->OnContactStatusChanged(cmd->GetPassport(), cmd->GetStatus());
}

void ServerConnection::ProcessFLN(MsnCommandWrapper* wrapper)
{
	InCommandFLN* cmd = (InCommandFLN*)wrapper->GetCommand();

	messenger->OnContactStatusChanged(cmd->GetPassport(), wxEmptyString);
}

void ServerConnection::ProcessCHG(MsnCommandWrapper* wrapper)
{
	InCommandCHG* cmd = (InCommandCHG*)wrapper->GetCommand();

	wxString status = cmd->GetStatus();
	messenger->OnUserStatusChanged(status);
}

void ServerConnection::ProcessCHL(MsnCommandWrapper* wrapper)
{
	InCommandCHL* cmd = dynamic_cast<InCommandCHL*>(wrapper->GetCommand());

    wxString str = cmd->GetChallengeString();
    str.Append(_T("Q1P7W2E4J9R8U3S5"));

	DEBUG(_T("challenge....") + str);

    unsigned char buf[16];
    wxString md5 = StringUtil::md5(str,buf);
    wxString result = StringUtil::ToHex(buf,16);

    OutCommandQRY qry;
    qry.SetClient(_T("msmsgs@msnmsgr.com"));
    qry.SetLength(result.Length());
    SendCommand(qry, result);
}

void ServerConnection::ProcessMSG(MsnCommandWrapper* wrapper)
{
	InCommandMSG* cmd = dynamic_cast<InCommandMSG*>(wrapper->GetCommand());

    MessageBuffer buffer(cmd->GetLength());

    //transport->ReadChunk(buffer.GetBuffer(), buffer.GetSize());

//	wxString profile(wrapper->GetPayloadBuffer(), wxConvUTF8, wrapper->GetPayloadLength());
//	DEBUG(_T("profile == ") + profile);

	wxMemoryBuffer& buf = wrapper->GetPayloadBuffer();

	MsnMessage* message = MsnMessageFactory::Get().ParseMessage((char*)buf.GetData(), buf.GetDataLen());

    if(message == NULL) return;

    DEBUG(_T("msn profile == ") + message->ToString());

	if(message->IsKindOf(CLASSINFO(ProfileMessage))){
		messenger->OnProfileMessageReceived((ProfileMessage*)message);
	}

	delete message;

}

void ServerConnection::ProcessPRP(MsnCommandWrapper* wrapper)
{
	InCommandPRP* cmd = dynamic_cast<InCommandPRP*>(wrapper->GetCommand());
    if(cmd->GetType().IsSameAs(_T("MFN"))){
        wxString name = cmd->GetValue();
        messenger->OnUserNameReceived(name);
    }
}

void ServerConnection::ProcessADC(MsnCommandWrapper* wrapper)
{
	InCommandADC* cmd = (InCommandADC*)wrapper->GetCommand();

	wxString list = cmd->GetList();
    if(list.IsSameAs(_T("FL"))){
		//ADC 9 FL N=tuxitty@hotmail.com F=tuxitty@hotmail.com C=e8e9deba-eaea-44e8-b563-b73c8e0fab72
        if(cmd->GetPassport().IsEmpty()){
            messenger->OnContactAddedToGroup(cmd->GetContactID(), cmd->GetGroupID());
        }else{	
			messenger->OnContactAddedToForwardList(cmd->GetPassport(), cmd->GetContactID(), cmd->GetName());
        }   
    }
	//ADC 0 RL N=tuxitty9@hotmail.com F=brutus
	else if(list.IsSameAs(_T("RL"))){
		//someone add me as friend
		messenger->OnContactAddedToReversedList(cmd->GetPassport(), cmd->GetName());
	}else if(list.IsSameAs(_T("AL"))){
		messenger->OnContactAddedToAllowedList(cmd->GetPassport());
	}else if(list.IsSameAs(_T("BL"))){
		messenger->OnContactAddedToBlockedList(cmd->GetPassport());
	}  	
}

void ServerConnection::ProcessADG(MsnCommandWrapper* wrapper)
{
	InCommandADG* cmd = (InCommandADG*) wrapper->GetCommand();
	messenger->OnGroupAdded(cmd->GetGroupID(), cmd->GetGroupName());
}

void ServerConnection::ProcessREM(MsnCommandWrapper* wrapper)
{
	InCommandREM* cmd = (InCommandREM*)wrapper->GetCommand();

	wxString list = cmd->GetList();

    if(list.IsSameAs(_T("FL"))){
        if(cmd->GetGroupID().IsEmpty())
			messenger->OnContactRemovedFromForwardList(cmd->GetContactID());
		else
			messenger->OnContactRemovedFromGroup(cmd->GetContactID(), cmd->GetGroupID());
    }

	else if(list.IsSameAs(_T("RL"))){
		//someone add me as friend
		messenger->OnContactRemovedFromReversedList(cmd->GetPassport());
	}else if(list.IsSameAs(_T("AL"))){
		messenger->OnContactRemovedFromAllowedList(cmd->GetPassport());
	}else if(list.IsSameAs(_T("BL"))){
		messenger->OnContactRemovedFromBlockedList(cmd->GetPassport());
	}  	
}

void ServerConnection::ProcessRMG(MsnCommandWrapper* wrapper)
{
	InCommandRMG* cmd = (InCommandRMG*) wrapper->GetCommand();
	messenger->OnGroupRemoved(cmd->GetGroupID());
}

void ServerConnection::ProcessREG(MsnCommandWrapper* wrapper)
{
	InCommandREG* cmd = (InCommandREG*) wrapper->GetCommand();
	messenger->OnGroupRenamed(cmd->GetGroupID(), cmd->GetGroupName());
}

int ServerConnection::OpenSessionConnection()
{
    ///connect to switch board
    OutCommandXFR xfr;
    SendCommand(xfr);

	return xfr.GetTransactionID();
}

void ServerConnection::ProcessXFR(MsnCommandWrapper* wrapper)
{
	InCommandXFR* cmd = dynamic_cast<InCommandXFR*>(wrapper->GetCommand());
    wxString address = cmd->GetServerAddress();
    int port = cmd->GetServerPort();

    if(cmd->IsNotification()){
        //connect to ns server
        ReConnect(address,port);
    }else{
        //connect to switch board
		wxString ticket = cmd->GetAuthString();

		int tranID = cmd->GetTransactionID();
		MsnSession* session = messenger->GetSessionManager()->FindSessionByTransaction(tranID);
		if(session != NULL){
			SessionConnection* conn = new SessionConnection(session, false);
			conn->SetUserPassport(userPassport);
			conn->SetTicket(ticket);

			conn->SetConnectionType(connectionType);
			conn->SetServerAddress(address);
			conn->SetServerPort(port);

			conn->SetProxyAddress(proxyAddress);
			conn->SetProxyPort(proxyPort);
			conn->SetProxyUsername(proxyUsername);
			conn->SetProxyPassword(proxyPassword);

			//TODO chekc result
			conn->Open();
		}else{
			//should have been deleted by user
		}
    }
}

void ServerConnection::ProcessRNG(MsnCommandWrapper* wrapper)
{
	InCommandRNG* cmd = (InCommandRNG*) wrapper->GetCommand();
    wxString sessionID = cmd->GetSessionID();
    wxString address = cmd->GetServerAddress();
    wxString ticket = cmd->GetTicket();
    wxString passport = cmd->GetInvitePassport();
    int port = cmd->GetServerPort();

	DEBUG(_T("open session with: ") + passport);

	MsnSession* session = messenger->GetSessionManager()->OpenSession(passport);

	//sometime when both sides start conversation, conflict 
	if(session->IsConnected()){
		return;
		//session->Close();
	}

    SessionConnection* conn = new SessionConnection(session, true);
    conn->SetUserPassport(userPassport);
    conn->SetTicket(ticket);
    conn->SetSessionID(sessionID);

	conn->SetConnectionType(connectionType);

	conn->SetServerAddress(address);
	conn->SetServerPort(port);
	conn->SetProxyAddress(proxyAddress);
	conn->SetProxyPort(proxyPort);
	conn->SetProxyUsername(proxyUsername);
	conn->SetProxyPassword(proxyPassword);

	conn->Open();
}
